package com.xuexiang.xui_lib.component.popupwindow;

import ohos.aafwk.ability.Ability;
import ohos.agp.animation.Animator;
import ohos.agp.components.AttrHelper;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.StackLayout;
import ohos.agp.components.Text;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.Rect;
import ohos.app.AbilityContext;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;

import static ohos.agp.components.Component.DrawTask.BETWEEN_BACKGROUND_AND_CONTENT;

public class ViewTooltip {
    private final Component mView;
    private final TooltipView mTooltipView;
    private final EventHandler mEventHandler;

    private ViewTooltip(Component view) {
        mView = view;
        mTooltipView = new TooltipView(getActivityContext(view.getContext()));
        mEventHandler = new EventHandler(EventRunner.getMainEventRunner());
    }

    /**
     * 创建并设置提示控件依附的View
     *
     * @param view 1
     * @return 1
     */
    public static ViewTooltip on(final Component view) {
        return new ViewTooltip(view);
    }

    public Context getActivityContext(Context context) {
        if (context instanceof AbilityContext) {
            return context;
        }
        return null;
    }

    /**
     * 设置提示显示的相对位置
     *
     * @param position 1
     * @return 1
     */
    public ViewTooltip position(Position position) {
        mTooltipView.setPosition(position);
        return this;
    }

    /**
     * 设置自定义提示布局
     *
     * @param customView 1
     * @return 1
     */
    public ViewTooltip customView(Component customView) {
        mTooltipView.setCustomView(customView);
        return this;
    }

    public ViewTooltip customView(int viewId) {
        mTooltipView.setCustomView(((Ability) mView.getContext()).findComponentById(viewId));
        return this;
    }

    public ViewTooltip align(ALIGN align) {
        mTooltipView.setAlign(align);
        return this;
    }

    /**
     * show
     *
     * @param decorView q
     * @return 1
     */
    public TooltipView show(ComponentContainer decorView) {
        final Context activityContext = decorView.getContext();
        if (activityContext != null) {
            mEventHandler.postTask(new Runnable() {
                @Override
                public void run() {
                    final Rect rect = new Rect();
                    rect.left = mView.getLeft();
                    rect.top = mView.getTop();
                    rect.right = mView.getRight();
                    rect.bottom = mView.getBottom();

                    int[] location = mView.getLocationOnScreen();
                    rect.left = location[0];

                    mTooltipView.setVisibility(Component.INVISIBLE);
                    decorView.addComponent(mTooltipView);
                    mEventHandler.postTask(new Runnable() {
                        @Override
                        public void run() {
                            mTooltipView.setVisibility(Component.VISIBLE);
                            mTooltipView.setupWithoutAnimation(rect, decorView.getWidth());
                        }
                    }, 50);
                }
            }, 100);
        }
        return mTooltipView;
    }

    public void close() {
        mTooltipView.close();
    }

    /**
     * 设置提示持续的时间
     *
     * @param duration duration
     * @return 1
     */
    public ViewTooltip duration(long duration) {
        mTooltipView.setDuration(duration);
        return this;
    }

    /**
     * 设置提示框的背景颜色
     *
     * @param color color
     * @return 1
     */
    public ViewTooltip color(Color color) {
        mTooltipView.setColor(color);
        return this;
    }

    /**
     * 设置显示的监听
     *
     * @param listener listener
     * @return 1
     */
    public ViewTooltip onDisplay(ListenerDisplay listener) {
        mTooltipView.setListenerDisplay(listener);
        return this;
    }

    /**
     * 设置隐藏的监听
     *
     * @param listener listener
     * @return 1
     */
    public ViewTooltip onHide(ListenerHide listener) {
        mTooltipView.setListenerHide(listener);
        return this;
    }

    /**
     * 设置间隔的距离
     *
     * @param left left
     * @param top top
     * @param right right
     * @param bottom bottom
     * @return 1
     */
    public ViewTooltip padding(int left, int top, int right, int bottom) {
        mTooltipView.mPaddingTop = top;
        mTooltipView.mPaddingBottom = bottom;
        mTooltipView.mPaddingLeft = left;
        mTooltipView.mPaddingRight = right;
        return this;
    }

    /**
     * 设置显示和隐藏的动画
     *
     * @param tooltipAnimation tooltipAnimation
     * @return 1
     */
    public ViewTooltip animation(TooltipAnimation tooltipAnimation) {
        mTooltipView.setTooltipAnimation(tooltipAnimation);
        return this;
    }

    /**
     * 设置提示的文字
     *
     * @param text q
     * @return 1
     */
    public ViewTooltip text(String text) {
        mTooltipView.setText(text);
        return this;
    }

    /**
     * 设置圆角的角度
     *
     * @param corner 1
     * @return 1
     */
    public ViewTooltip corner(int corner) {
        mTooltipView.setCorner(corner);
        return this;
    }

    /**
     * 设置提示文字的颜色
     *
     * @param textColor 1
     * @return 1
     */
    public ViewTooltip textColor(int textColor) {
        mTooltipView.setTextColor(new Color(textColor));
        return this;
    }

    public ViewTooltip textTypeFace(Font typeface) {
        mTooltipView.setTextTypeFace(typeface);
        return this;
    }

    /**
     * 设置提示文字的字体大小
     *
     * @param unit 1
     * @param textSize 1
     * @return 1
     */
    public ViewTooltip textSize(Text.TextSizeType unit, int textSize) {
        mTooltipView.setTextSize(unit, textSize);
        return this;
    }

    /**
     * 设置文字位置
     *
     * @param textGravity textGravity
     * @return return
     */
    public ViewTooltip setTextGravity(int textGravity) {
        mTooltipView.setTextGravity(textGravity);
        return this;
    }

    /**
     * 设置是否点击隐藏
     *
     * @param clickToHide 1
     * @return 1
     */
    public ViewTooltip clickToHide(boolean clickToHide) {
        mTooltipView.setClickToHide(clickToHide);
        return this;
    }

    public ViewTooltip autoHide(boolean autoHide, long duration) {
        mTooltipView.setAutoHide(autoHide);
        mTooltipView.setDuration(duration);
        return this;
    }

    /**
     * 显示的位置
     */
    public enum Position {
        LEFT,
        RIGHT,
        TOP,
        BOTTOM,
    }

    public enum ALIGN {
        START,
        CENTER,
    }

    public interface TooltipAnimation {
        void animateEnter(Component view, Animator.StateChangedListener animatorListener);

        void animateExit(Component view, Animator.StateChangedListener animatorListener);
    }

    /**
     * 显示监听
     */
    public interface ListenerDisplay {
        void onDisplay(Component view);
    }

    /**
     * 隐藏监听
     */
    public interface ListenerHide {
        void onHide(Component view);
    }

    public static class FadeTooltipAnimation implements TooltipAnimation {

        private long mFadeDuration = 400;

        public FadeTooltipAnimation() {
        }

        public FadeTooltipAnimation(long fadeDuration) {
            mFadeDuration = fadeDuration;
        }

        @Override
        public void animateEnter(Component view, Animator.StateChangedListener animatorListener) {
            view.setAlpha(0);
            view.createAnimatorProperty()
                .alpha(1)
                .setDuration(mFadeDuration)
                .setStateChangedListener(animatorListener)
                .start();
        }

        @Override
        public void animateExit(Component view, Animator.StateChangedListener animatorListener) {
            view.createAnimatorProperty()
                .alpha(0)
                .setDuration(mFadeDuration)
                .setStateChangedListener(animatorListener)
                .start();
        }
    }

    public static class TooltipView extends StackLayout {

        private static final int MARGIN_SCREEN_BORDER_TOOLTIP = 30;
        private static final int ARROW_HEIGHT = 15;
        protected Component mChildView;
        private Color mBubbleColor;
        private Path mBubblePath;
        private Paint mBubblePaint;
        private Position mPosition = Position.BOTTOM;
        private ALIGN mAlign = ALIGN.CENTER;
        private boolean mClickToHide;
        private boolean mAutoHide = true;
        private long mDuration = 4000;

        private ListenerDisplay mListenerDisplay;

        private ListenerHide mListenerHide;

        private TooltipAnimation mTooltipAnimation = new FadeTooltipAnimation();

        private int mCorner = 30;

        private int mPaddingTop;
        private int mPaddingBottom;
        private int mPaddingRight;
        private int mPaddingLeft;

        private Rect mViewRect;

        public TooltipView(Context context) {
            super(context);

            mBubbleColor = new Color(Color.getIntColor("#B2299EE3"));

            mChildView = new Text(context);
            ((Text) mChildView).setTextColor(Color.WHITE);
            ((Text) mChildView).setTextSize(AttrHelper.fp2px(14, mContext));
            ((Text) mChildView).setMultipleLine(true);
            addComponent(mChildView);
            mChildView.setPadding(0, 0, 0, 0);

            mBubblePaint = new Paint();
            mBubblePaint.setColor(mBubbleColor);
            mBubblePaint.setAntiAlias(true);
            mBubblePaint.setStyle(Paint.Style.FILL_STYLE);

            mPaddingTop = mPaddingBottom = AttrHelper.vp2px(10, getContext());
            mPaddingRight = mPaddingLeft = AttrHelper.vp2px(10, getContext());

//            setTextTypeFace(XUI.getDefaultTypeface());

//            setEstimateSizeListener(new EstimateSizeListener() {
//                @Override
//                public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
//
//                    int widthSpecSize = EstimateSpec.getSize(widthMeasureSpec);
//                    int heightSpecSize = EstimateSpec.getSize(heightMeasureSpec);
//
//                    int childHeight = heightSpecSize - getPaddingTop() - getPaddingBottom();
//                    int resultWidthSize = 0;
//                    resultWidthSize = widthSpecSize;
//
//                    //设置文本大小
//                    final Component child = getComponentAt(0);
//                    int childWidthMeasureSpec = EstimateSpec.getSizeWithMode(widthSpecSize, EstimateSpec.PRECISE);
//                    int childHeightMeasureSpec = EstimateSpec.getSizeWithMode(childHeight, EstimateSpec.NOT_EXCEED);
//                    child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
//
//                    setEstimatedSize(resultWidthSize, childHeightMeasureSpec);
//                    return true;
//                }
//            });

            addDrawTask(new DrawTask() {
                @Override
                public void onDraw(Component component, Canvas canvas) {
                    if (mBubblePath != null) {
                        canvas.drawPath(mBubblePath, mBubblePaint);
                    }
                }
            }, BETWEEN_BACKGROUND_AND_CONTENT);
        }

        private void measureChild(Component child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) {
            ComponentContainer.LayoutConfig lc = child.getLayoutConfig();
            int childWidthMeasureSpec = EstimateSpec.getChildSizeWithMode(
                lc.width, parentWidthMeasureSpec, EstimateSpec.UNCONSTRAINT);
            int childHeightMeasureSpec = EstimateSpec.getChildSizeWithMode(
                lc.height, parentHeightMeasureSpec, EstimateSpec.UNCONSTRAINT);
            child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
        }

        public void setCustomView(Component customView) {
            removeComponent(mChildView);
            mChildView = customView;
            addComponent(mChildView, ComponentContainer.LayoutConfig.MATCH_CONTENT, ComponentContainer.LayoutConfig.MATCH_CONTENT);
        }

        public void setColor(Color color) {
            mBubbleColor = color;
            mBubblePaint.setColor(color);
            invalidate();
        }

        public void setPosition(Position position) {
            mPosition = position;
            switch (position) {
                case TOP:
                    setPaddingRelative(mPaddingLeft, mPaddingTop, mPaddingRight, mPaddingBottom + ARROW_HEIGHT);
                    break;
                case BOTTOM:
                    setPaddingRelative(mPaddingLeft, mPaddingTop + ARROW_HEIGHT, mPaddingRight, mPaddingBottom);
                    break;
                case LEFT:
                    setPaddingRelative(mPaddingLeft, mPaddingTop, mPaddingRight + ARROW_HEIGHT, mPaddingBottom);
                    break;
                case RIGHT:
                    setPaddingRelative(mPaddingLeft + ARROW_HEIGHT, mPaddingTop, mPaddingRight, mPaddingBottom);
                    break;
                default:
                    break;
            }
            invalidate();
        }

        public void setAlign(ALIGN align) {
            mAlign = align;
            invalidate();
        }

        public void setText(String text) {
            if (mChildView instanceof Text) {
//                ((Text) mChildView).setText(Html.fromHtml(text));
                ((Text) mChildView).setText(text);
//                ShapeElement shapeElement = new ShapeElement();
//                shapeElement.setRgbColor(RgbColor.fromArgbInt(Color.RED.getValue()));
//                ((Text) mChildView).setBackground(shapeElement);
            }
            invalidate();
        }

        public void setTextColor(Color textColor) {
            if (mChildView instanceof Text) {
                ((Text) mChildView).setTextColor(textColor);
            }
            invalidate();
        }

        public void setTextTypeFace(Font textTypeFace) {
            if (mChildView instanceof Text) {
                ((Text) mChildView).setFont(textTypeFace);
            }
            invalidate();
        }

        public void setTextSize(Text.TextSizeType unit, int size) {
            if (mChildView instanceof Text) {
                ((Text) mChildView).setTextSize(size, unit);
            }
            invalidate();
        }

        public void setTextGravity(int textGravity) {
            if (mChildView instanceof Text) {
                ((Text) mChildView).setTextAlignment(textGravity);
            }
            invalidate();
        }

        public void setClickToHide(boolean clickToHide) {
            mClickToHide = clickToHide;
        }

        public void setCorner(int corner) {
            mCorner = corner;
        }

        public void setListenerDisplay(ListenerDisplay listener) {
            mListenerDisplay = listener;
        }

        public void setListenerHide(ListenerHide listener) {
            mListenerHide = listener;
        }

        public void setTooltipAnimation(TooltipAnimation tooltipAnimation) {
            mTooltipAnimation = tooltipAnimation;
        }

        protected void startEnterAnimation() {
            mTooltipAnimation.animateEnter(this, new Animator.StateChangedListener() {
                @Override
                public void onStart(Animator animator) {

                }

                @Override
                public void onStop(Animator animator) {

                }

                @Override
                public void onCancel(Animator animator) {

                }

                @Override
                public void onEnd(Animator animator) {
                    if (mListenerDisplay != null) {
                        mListenerDisplay.onDisplay(TooltipView.this);
                    }
                }

                @Override
                public void onPause(Animator animator) {

                }

                @Override
                public void onResume(Animator animator) {

                }
            });
        }

        protected void startExitAnimation(final Animator.StateChangedListener animatorListener) {
            mTooltipAnimation.animateExit(this, new Animator.StateChangedListener() {
                @Override
                public void onStart(Animator animator) {

                }

                @Override
                public void onStop(Animator animator) {

                }

                @Override
                public void onCancel(Animator animator) {

                }

                @Override
                public void onEnd(Animator animator) {
                    animatorListener.onEnd(animator);
                    if (mListenerHide != null) {
                        mListenerHide.onHide(TooltipView.this);
                    }
                }

                @Override
                public void onPause(Animator animator) {

                }

                @Override
                public void onResume(Animator animator) {

                }
            });
        }

        protected void handleAutoRemove() {
            if (mClickToHide) {
                setClickedListener(new ClickedListener() {
                    @Override
                    public void onClick(Component component) {
                        if (mClickToHide) {
                            remove();
                        }
                    }
                });
            }

            if (mAutoHide) {
                new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                    @Override
                    public void run() {
                        remove();
                    }
                }, mDuration);
            }
        }

        public void remove() {
            startExitAnimation(new Animator.StateChangedListener() {
                @Override
                public void onStart(Animator animator) {

                }

                @Override
                public void onStop(Animator animator) {

                }

                @Override
                public void onCancel(Animator animator) {

                }

                @Override
                public void onEnd(Animator animator) {
                    if (getComponentParent() != null) {
                        final ComponentContainer parent = (ComponentContainer) getComponentParent();
                        parent.removeComponent(TooltipView.this);
                    }
                }

                @Override
                public void onPause(Animator animator) {

                }

                @Override
                public void onResume(Animator animator) {

                }
            });
        }

        public void setDuration(long duration) {
            mDuration = duration;
        }

        public void setAutoHide(boolean autoHide) {
            mAutoHide = autoHide;
        }

        public void setupPosition(Rect rect) {

            if (mPosition == Position.LEFT || mPosition == Position.RIGHT) {

                final int myHeight = getHeight();
                final int hisHeight = rect.getHeight();

                final int maxHeight = Math.max(hisHeight, myHeight);
                final int minHeight = Math.min(hisHeight, myHeight);

                int spacingY = 0;
                switch (mAlign) {
                    case START:
                        spacingY = 0;
                        break;
                    //case END:
                    //    spacingY = maxHeight - minHeight;
                    //    break;
                    case CENTER:
                        spacingY = (int) (-1f * maxHeight / 2f + minHeight / 2f);
                        break;
                    default:
                        break;
                }

                if (mPosition == Position.LEFT) {
                    setTranslationY(rect.top + spacingY);
                    setTranslationX(rect.left - getEstimatedWidth());
                } else {
                    setTranslationY(rect.top + spacingY);
                    setTranslationX(rect.right);
                }

            } else {
                int spacingX = 0;

                final int myWidth = getEstimatedWidth();
                final int hisWidth = rect.getWidth();

                if (mAlign == ALIGN.CENTER) {
                    spacingX = (int) (hisWidth / 2f - 1f * myWidth / 2f);
                }

                if (mPosition == Position.BOTTOM) {
                    setTranslationY(rect.bottom);
                    setTranslationX(rect.left + spacingX);
                } else {
                    setTranslationY(rect.top - getHeight());
                    setTranslationX(rect.left + spacingX);
                }
            }
        }

        private Path drawBubble(Rect myRect, float topLeftDiameter, float topRightDiameter, float bottomRightDiameter, float bottomLeftDiameter) {
            final Path path = new Path();

            if (mViewRect == null) {
                return path;
            }

            topLeftDiameter = topLeftDiameter < 0 ? 0 : topLeftDiameter;
            topRightDiameter = topRightDiameter < 0 ? 0 : topRightDiameter;
            bottomLeftDiameter = bottomLeftDiameter < 0 ? 0 : bottomLeftDiameter;
            bottomRightDiameter = bottomRightDiameter < 0 ? 0 : bottomRightDiameter;

            final float spacingLeft = mPosition == Position.RIGHT ? ARROW_HEIGHT : 0;
            final float spacingTop = mPosition == Position.BOTTOM ? ARROW_HEIGHT : 0;
            final float spacingRight = mPosition == Position.LEFT ? ARROW_HEIGHT : 0;
            final float spacingBottom = mPosition == Position.TOP ? ARROW_HEIGHT : 0;

            final float left = spacingLeft + myRect.left;
            final float top = spacingTop + myRect.top;
            final float right = myRect.right - spacingRight;
            final float bottom = myRect.bottom - spacingBottom;
            final float centerX = mViewRect.getCenterX() - getContentPositionX();

            path.moveTo(left + topLeftDiameter / 2f, top);
            //LEFT, TOP

            int ARROW_WIDTH = 15;
            if (mPosition == Position.BOTTOM) {
                path.lineTo(centerX - ARROW_WIDTH, top);
                path.lineTo(centerX, myRect.top);
                path.lineTo(centerX + ARROW_WIDTH, top);
            }
            path.lineTo(right - topRightDiameter / 2f, top);

            path.quadTo(right, top, right, top + topRightDiameter / 2);
            //RIGHT, TOP

            if (mPosition == Position.LEFT) {
                path.lineTo(right, bottom / 2f - ARROW_WIDTH);
                path.lineTo(myRect.right, bottom / 2f);
                path.lineTo(right, bottom / 2f + ARROW_WIDTH);
            }
            path.lineTo(right, bottom - bottomRightDiameter / 2);

            path.quadTo(right, bottom, right - bottomRightDiameter / 2, bottom);
            //RIGHT, BOTTOM

            if (mPosition == Position.TOP) {
                path.lineTo(centerX + ARROW_WIDTH, bottom);
                path.lineTo(centerX, myRect.bottom);
                path.lineTo(centerX - ARROW_WIDTH, bottom);
            }
            path.lineTo(left + bottomLeftDiameter / 2, bottom);

            path.quadTo(left, bottom, left, bottom - bottomLeftDiameter / 2);
            //LEFT, BOTTOM

            if (mPosition == Position.RIGHT) {
                path.lineTo(left, bottom / 2f + ARROW_WIDTH);
                path.lineTo(myRect.left, bottom / 2f);
                path.lineTo(left, bottom / 2f - ARROW_WIDTH);
            }
            path.lineTo(left, top + topLeftDiameter / 2);

            path.quadTo(left, top, left + topLeftDiameter / 2, top);

            path.close();

            return path;
        }

        public boolean adjustSize(Rect rect, int screenWidth) {
            boolean changed = false;
            final ComponentContainer.LayoutConfig layoutParams = getLayoutConfig();
            if (mPosition == Position.LEFT && getEstimatedWidth() > rect.left) {
                layoutParams.width = rect.left - MARGIN_SCREEN_BORDER_TOOLTIP;
                changed = true;
            } else if (mPosition == Position.RIGHT && rect.right + getEstimatedWidth() > screenWidth) {
                layoutParams.width = screenWidth - rect.right - MARGIN_SCREEN_BORDER_TOOLTIP;
                changed = true;
            } else if (mPosition == Position.TOP || mPosition == Position.BOTTOM) {
                float widthRight = (getEstimatedWidth() - rect.getWidth()) / 2f;
                if (rect.right + widthRight > screenWidth) {
                    final float movinX = (rect.right + widthRight) - screenWidth + 30;
                    rect.left -= movinX;
                    rect.right -= movinX;
                    changed = true;
                } else if (rect.left - widthRight < 0) {
                    final float movinX = 0 - (rect.left - widthRight) + 30;
                    rect.left += movinX;
                    rect.right += movinX;
                    changed = true;
                }
            }
            setLayoutConfig(layoutParams);
            invalidate();
            return changed;
        }

        private void onSetup(Rect myRect) {
            setupPosition(myRect);
            mBubblePath = drawBubble(new Rect(0, 0, getWidth(), getHeight()), mCorner, mCorner, mCorner, mCorner);
            startEnterAnimation();
            handleAutoRemove();
        }

        public void setupWithoutAnimation(final Rect viewRect, int screenWidth) {
            mViewRect = new Rect(viewRect);
            final Rect myRect = new Rect(viewRect);
            adjustSize(myRect, screenWidth);
            setupPosition(myRect);

            new EventHandler(EventRunner.getMainEventRunner()).postTask(new Runnable() {
                @Override
                public void run() {
                    setup(mViewRect, screenWidth);
                }
            }, 50);
        }

        public void setup(final Rect viewRect, int screenWidth) {
//            mViewRect = new Rect(viewRect);
            final Rect myRect = new Rect(viewRect);

            adjustSize(myRect, screenWidth);
            onSetup(myRect);
        }

        public void close() {
            remove();
        }
    }
}
